About this notebook This notebook is part of analysis of evaluate the impact of different reference bundle on scRNA-Seq quantification. # Annotation First we need to use package rtracklayer to load several gene annotation gtf files. * Ensembl * refseq * gencodeV27

library('ggplot2')
library('rtracklayer')
system.time(gtf_gencode_comp <- readGFF("~/Documents/HCA/reference/refs/gencode.v27.chr_patch_hapl_scaff.annotation.gtf", version=2L, tags = c("gene_name","gene_id", "transcript_id","gene_type")))
   user  system elapsed 
 16.114   0.993  17.335 
system.time(gtf_gencode_basic <- readGFF("~/Documents/HCA/reference/refs/gencode.v27.chr_patch_hapl_scaff.basic.annotation.gtf", version=2L, tags = c("gene_name","gene_id", "transcript_id","gene_type")))
   user  system elapsed 
 10.027   0.576  10.658 
system.time(gtf_ensembl <- readGFF("~/Documents/HCA/reference/refs/Homo_sapiens.GRCh38.90.gtf", version=2L,tags = c("gene_name","gene_id", "transcript_id","gene_biotype")))
   user  system elapsed 
 14.738   0.773  15.573 
system.time(gtf_refseq <- readGFF("~/Documents/HCA/reference/refs/ncbi-genomes-2017-10-05/GCF_000001405.37_GRCh38.p11_genomic.gff.gz", tags = c("ID", "Name","gbkey","gene","gene_biotype","Parent")))
   user  system elapsed 
 15.570   0.585  16.162 

some header of gtf files

tags<-c('GeneID','Chr','Start','End','Strand','Length','Counts')
head(gtf_ensembl)

Impact on gene quantification overview

a set of gene quantification results is shown as below. First the gene counts from ensembl bundle. The SRR1294900_25_GRCh38_Ensembl.gene.unq.counts.txt is generated from featureCounts with unique option

load Ensembl bundle results

ensembl.counts<-read.delim('/Users/jishuxu/Documents/HCA/reference/counts/SRR1294900_25_GRCh38_Ensembl.gene.unq.counts.txt',sep='\t',header=T,skip=1)
colnames(ensembl.counts)<-tags
mlist<-match(ensembl.counts$GeneID,gtf_ensembl$gene_id)
geneName<-gtf_ensembl$gene_name[mlist]
btype<-gtf_ensembl$gene_biotype[mlist]
c1<-data.frame("geneName"=geneName,'Ensembl.Counts'=log(ensembl.counts$Counts+1,base=2),'Ensembl.type'=gtf_ensembl$gene_biotype[mlist])
head(c1)

If we define detected genes as genes with coverage >0 and good coverage genes as genes with coverage >5. Then we can plot the top 10 biotype categories.

gene.detects<-list()
t1<-sort(table(c1[c1$Ensembl.Counts>0,3]),decreasing=T)
t2<-sort(table(c1[c1$Ensembl.Counts>5,3]),decreasing=T)
gene.detects[['ensembl']]<-cbind('detected.genes'=t1[1:10],'good.genes'=t2[1:10])

load RefSeq bundle results

Then the gene counts from RefSeq.

refseq.counts<-read.delim('/Users/jishuxu/Documents/HCA/reference/counts/SRR1294900_25_GRCh38_RefSeq.gene.unq.counts.txt',sep='\t',header=T,skip=1)
colnames(refseq.counts)<-tags
mlist<-match(refseq.counts$GeneID,gtf_refseq$ID)
geneName<-gtf_refseq$Name[mlist]
c2<-data.frame("geneName"=geneName,"RefSeq.Counts"=log(refseq.counts$Counts+1,base=2),'RefSeq.type'=gtf_refseq$gene_biotype[mlist])
head(c2)

summary detected genes and good genes based on biotype.

t1<-sort(table(c2[c2$RefSeq.Counts>0,3]),decreasing=T)
t2<-sort(table(c2[c2$RefSeq.Counts>5,3]),decreasing=T)
gene.detects[['refseq']]<-cbind('detected.genes'=t1[1:10],'good.genes'=t2[1:10])

load gencode bundle results

  • For GencodeV27, there are two reference bundle GencodeV27 Comprehansive and GencodeV27 Basic. Repeat the similar analysis.
gencode.counts.comp<-read.delim('/Users/jishuxu/Documents/HCA/reference/counts/SRR1294900_25_GRCh38_GencodeV27.gene.unq.counts.txt',sep='\t',header=T,skip=1)
colnames(gencode.counts.comp)<-tags
mlist<-match(gencode.counts.comp$GeneID,gtf_gencode_comp$gene_id)
geneName<-gtf_gencode_comp$gene_name[mlist]
c3<-data.frame("geneName"=geneName,"GencodeComp.Counts"=log(gencode.counts.comp$Counts+1,base=2),'Gencode.type'=gtf_gencode_comp$gene_type[mlist])
dim(c3)
[1] 63925     3
gencode.counts.basic<-read.delim('/Users/jishuxu/Documents/HCA/reference/counts/SRR1294900_25_GRCh38_GencodeV27_basic.gene.unq.counts.txt',sep='\t',header=T,skip=1)
colnames(gencode.counts.basic)<-tags
mlist<-match(gencode.counts.basic$GeneID,gtf_gencode_basic$gene_id)
geneName<-gtf_gencode_basic$gene_name[mlist]
c4<-data.frame("geneName"=geneName,"GencodeBasic.Counts"=log(gencode.counts.basic$Counts+1,base=2),'Gencode.type'=gtf_gencode_basic$gene_type[mlist])
dim(c4)
[1] 63925     3
  • Summary gene coverage based on biotype. For Comprehansive GencodeV27
t1<-sort(table(c3[c3$GencodeComp.Counts>0,3]),decreasing=T)
t2<-sort(table(c3[c3$GencodeComp.Counts >5,3]),decreasing=T)
gene.detects[['GencodeComp']]<-cbind('detected.genes'=t1[1:10],'good.genes'=t2[1:10])
  • Then Basic GencodeV27
t1<-sort(table(c4[c4$GencodeBasic.Counts>0,3]),decreasing=T)
t2<-sort(table(c4[c4$GencodeBasic.Counts >5,3]),decreasing=T)
gene.detects[["GencodeBasic"]]<-cbind('detected.genes'=t1[1:10],'good.genes'=t2[1:10])

summary all 4 reference bundle together.

Now we can summary on detected genes by biotype cross alll 4 reference bundle.

##top 3 biotype
library(reshape2)
bios<-c('protein_coding','processed_pseudogene','pseudogene','lincRNA','lncRNA')
g.ensembl<-subset(gene.detects$ensembl,rownames(gene.detects$ensembl) %in% bios)
g.refseq<-subset(gene.detects$refseq,rownames(gene.detects$refseq) %in% bios)
g.gencode<-subset(gene.detects$GencodeComp,rownames(gene.detects$GencodeComp) %in% bios)
g.gencodeBasic<-subset(gene.detects$GencodeBasic,rownames(gene.detects$GencodeBasic) %in% bios)
detects<-data.frame('biotype'=rep(c('protein_coding','pseudogene','lncRNA'),4),rbind(g.ensembl,g.refseq,g.gencode,g.gencodeBasic),'bundle'=rep(c('Ensembl','RefSeq','Gencode','GencodeV27'),c(3,3,3,3)))
some row.names duplicated: 4,7,8,9,10,11,12 --> row.names NOT used
m.detects<-melt(detects)
Using biotype, bundle as id variables
##detected >0X
ggplot(data=m.detects,aes(x=biotype,y=value,fill=bundle))+geom_bar(stat="identity",position=position_dodge())+facet_grid(variable~.,scales="free_y")+geom_text(aes(biotype, value, label = value),position = position_dodge(width = 1)) 

Evaluate Dropout, overdispersion events

Ensembl vs RefSeq

We compare the gene quantification from Ensembl and RefSeq reference bundles. We are looking for 3 type of events, drop-out if only detectable from one reference bundle but not another, predictable if one can be predicted or estimated by another or the observation in one result can fall into prediction interval based on regression model,overdispersion if neither dropout nor predictable, overdispersion generally represent some genes have very high expression in one results but very low expression in another. - First merge two quantification by geneName or geneID.

ensembl.refseq<-merge(c1,c2,by=c('geneName'),all=F,all.x=F,all.y=F)
dim(ensembl.refseq)
[1] 32647     5

Overlapping genes ID between Ensembl and RefSeq is 32647. - Let’s examine the association between Ensembl and RefSeq quantification. Before the regression analysis, we need to remove drop-out genes from both ensembl and refseq.

subdata<-subset(ensembl.refseq,Ensembl.Counts>0 & RefSeq.Counts >0)
fit<-lm(RefSeq.Counts~Ensembl.Counts, data=subdata)
summary(fit)

Call:
lm(formula = RefSeq.Counts ~ Ensembl.Counts, data = subdata)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.6304 -0.0886  0.0881  0.1788  7.6481 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)     0.16485    0.02844   5.796 7.22e-09 ***
Ensembl.Counts  0.96185    0.00410 234.607  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.8881 on 4967 degrees of freedom
Multiple R-squared:  0.9172,    Adjusted R-squared:  0.9172 
F-statistic: 5.504e+04 on 1 and 4967 DF,  p-value: < 2.2e-16

The linear model is proper.

  • Let’s estimate the 95% prediction interval: For a data point
pred<-predict(fit,data=ensembl.refseq,level=0)
conf_interval_3 <- predict(fit, newdata=data.frame(Ensembl.Counts=100), interval="prediction",level = 0.95)
conf_interval_3
       fit      lwr      upr
1 96.34994 94.45249 98.24739

Then for regression line

conf_interval <- predict(fit, newdata=ensembl.refseq, interval="prediction",level = 0.95)

If data point can not fall into this 95% prediction intervals, it should be counted as overdispersion - let’s plot results with scatterplot

##this function just label model in plot
equation = function(x) {
  lm_coef <- list(a = round(coef(x)[1], digits = 2),
                  b = round(coef(x)[2], digits = 2),
                  r2 = round(summary(x)$r.squared, digits = 2));
  lm_eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(R)^2~"="~r2,lm_coef)
  as.character(as.expression(lm_eq));                 
}
##label with color
type<-rep('normal',nrow(ensembl.refseq))
type[ensembl.refseq$Ensembl.Counts==0 & ensembl.refseq$RefSeq.Counts>0]<-'dropout.ensembl'
type[ensembl.refseq$Ensembl.Counts>0 & ensembl.refseq$RefSeq.Counts==0]<-'dropout.refseq'
type[(ensembl.refseq$RefSeq.Counts > conf_interval[,3]|ensembl.refseq$RefSeq.Counts<conf_interval[,2])&(ensembl.refseq$Ensembl.Counts>0 & ensembl.refseq$RefSeq.Counts>0)]<-'overdispersion' 
ensembl.refseq$type<-type
## label prefiction model in plot
eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(r)^2~"="~r2, list(a = format(coef(fit)[1], digits = 2), b = format(coef(fit)[2], digits = 2), r2 = format(summary(fit)$r.squared, digits = 3)))
## scatter plot
p1 <- ggplot(ensembl.refseq,aes(x=Ensembl.Counts,y=RefSeq.Counts),color="grey")
p1<-p1+geom_ribbon(aes(ymin=conf_interval[,2],ymax=conf_interval[,3]),alpha=0.2,fill="red")
p1<-p1+geom_point(data=ensembl.refseq,aes(x=Ensembl.Counts,y=RefSeq.Counts,color=type))
p1<-p1+annotate("text", x = 4.1, y = 12, label = equation(fit), parse = TRUE)
p1<-p1+xlab("log2(Ensembl.Counts+1)")+ylab("log2(RefSeq+1)")
p1

  • Count drop-out genes in Ensembl compared to RefSeq
dropout.ensembl<-subset(ensembl.refseq,Ensembl.Counts==0 & RefSeq.Counts>0)
dim(dropout.ensembl)
[1] 325   6
## barplot
x<-data.frame(sort(table(dropout.ensembl$Ensembl.type),decreasing=T)[1:10])
colnames(x)<-c('Biotype','Counts')
ggplot(data.frame(x),aes(x=Biotype,y=Counts))+geom_bar(aes(fill = Biotype), position = "dodge", stat="identity")+xlab('Biotype')+ylab("# of dropout genes")+geom_text(aes(label=Counts), vjust=1.6, color="black", size=3.5) +theme(axis.text.x=element_blank())+ggtitle('dropout genes in Ensembl')

  • We want to know whether the dropout genes in Ensembl have any biological functional meaning. We overlap dropout genes with MSigDB C2_curated geneset, C2_curated geneset contain most KEGG pathway. two way to calculate the geneset overlapping, Fisher-exact test and hyper-geometry test. We use both tests.
library(GeneOverlap)
library(MSigDB)
## create function to do overlapping test
gsOverlap<-function(gs_cureated,gs){
  ##msigdb_c2<-MSigDB[["C2_CURATED"]]
  pvals<-c()
  for(i in names(gs_curated)){
    go.obj <- newGeneOverlap(gs_curated[[i]], gs, genome.size=60000)
    ##fisher exact
    go.obj <- testGeneOverlap(go.obj)
    overlap<-length(getIntersection(go.obj))
    PopSize<-60000
    f.pval<-getPval(go.obj)
    list1<-length(gs)
    list2<-length(gs_curated[[i]])
    ##hyperGT
    if(overlap>1){
     h.pval<-phyper(overlap-1,list1,PopSize-list1,list2,lower.tail = FALSE, log.p = FALSE)
  }else{
    h.pval<-1
    }
    pvals<-rbind(pvals,c(i,overlap,list1,list2,f.pval,h.pval))
  }
  colnames(pvals)<-c('gsName','overlap','dropout.gs.length','msigdb.gs.length','fisher','hyper')
  pvals<-data.frame(pvals)
  pvals$fisher.adj<-p.adjust(pvals$fisher)
  pvals$hyper.adj<-p.adjust(pvals$hyper)
  return(pvals)
}
## do test : ensembl
gs_curated<-MSigDB[["C2_CURATED"]]
gs<-dropout.ensembl$geneName
res<-gsOverlap(gs_curated,gs)
head(res)
sum(res$fisher.adj<0.05)
[1] 0
sum(res$hyper.adj<0.05)
[1] 0
## hypherGtest

So there is no significant enrichment with any genesets in C2. And repeat this analysis with the dropout genes in RefSeq

dropout.refseq<-subset(ensembl.refseq,Ensembl.Counts>0 & RefSeq.Counts==0)
dim(dropout.refseq)
[1] 954   6
##barplot(sort(table(dropout.refseq$RefSeq.type),decreasing=T)[1:10],main="Dropout genes: RefSeq")
x<-data.frame(sort(table(dropout.refseq$RefSeq.type),decreasing=T)[1:10])
colnames(x)<-c('Biotype','Counts')
ggplot(data.frame(x),aes(x=Biotype,y=Counts))+geom_bar(aes(fill = Biotype), position = "dodge", stat="identity")+xlab('Biotype')+ylab("# of dropout genes")+geom_text(aes(label=Counts), vjust=1.6, color="black", size=3.5) +theme(axis.text.x=element_blank())+ggtitle('dropout genes in RefSeq')

RefSeq’s dropout genes have any functional meaning?

gs<-dropout.refseq$geneName
res<-gsOverlap(gs_curated,gs)
head(res)
sum(res$fisher.adj<0.05)
[1] 0
sum(res$hyper.adj<0.05)
[1] 0

GencodeV27Basic vs refSeq

  • We repeat the analysis to compare GencodeV27Basic with RefSeq.
gencode.refseq<-merge(c2,c4,by=c('geneName'),all=F, all.x=F,all.y=F)
dim(gencode.refseq)
[1] 57943     5
  • Regression model
subdata<-subset(gencode.refseq,GencodeBasic.Counts>0 & RefSeq.Counts >0)
fit<-lm(RefSeq.Counts~GencodeBasic.Counts, data=subdata)
summary(fit)

Call:
lm(formula = RefSeq.Counts ~ GencodeBasic.Counts, data = subdata)

Residuals:
    Min      1Q  Median      3Q     Max 
-9.4115 -0.1340 -0.0216  0.0356  9.3393 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)         0.214134   0.021139   10.13   <2e-16 ***
GencodeBasic.Counts 0.973297   0.003087  315.27   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.6724 on 4939 degrees of freedom
Multiple R-squared:  0.9527,    Adjusted R-squared:  0.9527 
F-statistic: 9.94e+04 on 1 and 4939 DF,  p-value: < 2.2e-16
  • estimate 95% prediction interval. For a data point
pred<-predict(fit,data=gencode.refseq)
conf_interval_3 <- predict(fit, newdata=data.frame(GencodeBasic.Counts=100), interval="prediction",level = 0.95)
conf_interval_3
       fit      lwr      upr
1 97.54379 96.10816 98.97943

95% prediction intervals for regression line

conf_interval <- predict(fit, newdata=gencode.refseq, interval="prediction",level = 0.95)
  • visualize results in scatterplot
##label with color
type<-rep('normal',nrow(gencode.refseq))
type[gencode.refseq$GencodeBasic.Counts==0 & gencode.refseq$RefSeq.Counts>0]<-'dropout.gencode'
type[gencode.refseq$GencodeBasic.Counts>0 & gencode.refseq$RefSeq.Counts==0]<-'dropout.refseq'
type[(gencode.refseq$RefSeq.Counts > conf_interval[,3] | gencode.refseq$RefSeq.Counts<conf_interval[,2])&(gencode.refseq$GencodeBasic.Counts>0 & gencode.refseq$RefSeq.Counts>0)]<-'overdispersion'
gencode.refseq$type<-type
table(gencode.refseq$type)

dropout.gencode  dropout.refseq          normal  overdispersion 
            483             367           56918             175 
## label prefiction model in plot
eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(r)^2~"="~r2, list(a = format(coef(fit)[1], digits = 2), b = format(coef(fit)[2], digits = 2), r2 = format(summary(fit)$r.squared, digits = 3)))
## scatter plot
p1 <- ggplot(gencode.refseq,aes(x=GencodeBasic.Counts,y=RefSeq.Counts),color="grey")
p1<-p1+geom_ribbon(aes(ymin=conf_interval[,2],ymax=conf_interval[,3]),alpha=0.2,fill="red")
p1<-p1+geom_point(data=gencode.refseq,aes(x=GencodeBasic.Counts,y=RefSeq.Counts,color=type))
p1<-p1+annotate("text", x = 4.1, y = 12, label = equation(fit), parse = TRUE)
p1<-p1+xlab("log2(GencodeBasic.Counts+1)")+ylab("log2(RefSeq.Counts+1)")
p1

  • Count drop-out genes in GencodeV27 basic compared to RefSeq
dropout.gencode<-subset(gencode.refseq,GencodeBasic.Counts==0 & RefSeq.Counts>0)
dim(dropout.gencode)
[1] 483   6
#barplot(sort(table(dropout.gencode$Gencode.type),decreasing=T)[1:10],main="Dropout genes: GencodeBasic")
x<-data.frame(sort(table(dropout.gencode$Gencode.type),decreasing=T)[1:10])
colnames(x)<-c('Biotype','Counts')
ggplot(data.frame(x),aes(x=Biotype,y=Counts))+geom_bar(aes(fill = Biotype), position = "dodge", stat="identity")+xlab('Biotype')+ylab("# of dropout genes")+geom_text(aes(label=Counts), vjust=1.6, color="black", size=3.5) +theme(axis.text.x=element_blank())+ggtitle('dropout gene in Gencode')

whether dropout genes have any functional meaning?

gs<-dropout.gencode$geneName
gs_curated<-MSigDB[["C2_CURATED"]]
res<-gsOverlap(gs_curated,gs)
head(res)
sum(res$fisher.adj<0.05)
[1] 0
sum(res$hyper.adj<0.05)
[1] 0

Then the drop-out genes in RefSeq compared to GencodeV27 Basic

dropout.refseq<-subset(gencode.refseq,GencodeBasic.Counts>0 & RefSeq.Counts==0)
dim(dropout.refseq)
[1] 367   6
##barplot(sort(table(dropout.refseq$RefSeq.type),decreasing=T)[1:10],main="Dropout genes: RefSeq")
x<-data.frame(sort(table(dropout.refseq$RefSeq.type),decreasing=T)[1:10])
colnames(x)<-c('Biotype','Counts')
ggplot(data.frame(x),aes(x=Biotype,y=Counts))+geom_bar(aes(fill = Biotype), position = "dodge", stat="identity")+xlab('Biotype')+ylab("# of dropout genes")+geom_text(aes(label=Counts), vjust=1.6, color="black", size=3.5) +theme(axis.text.x=element_blank())+ggtitle('dropout genes in refseq')

Whether dropout genes have functional meaning?

gs<-dropout.refseq$geneName
gs_curated<-MSigDB[["C2_CURATED"]]
res<-gsOverlap(gs_curated,gs)
head(res)
sum(res$fisher.adj<0.05)
[1] 0
sum(res$hyper.adj<0.05)
[1] 0

4-ways overlapping

library('VennDiagram')
g1<-c1[c1$Ensembl.Counts>0,1]
g2<-c2[c2$RefSeq.Counts>0,1]
g3<-c3[c3$GencodeComp.Counts>0,1]
g4<-c4[c4$GencodeBasic.Counts>0,1]
venn.plot<-venn.diagram(x = list('Ensembl'=g1,'RefSeq'=g2,'Gencode'=g3,'GencodeBasic'=g4), imagetype='png',filename = "Venn.png",
col = "transparent", fill = c("cornflowerblue","green","yellow","darkorchid1"),
label.col = c("orange", "white", "darkorchid4", "white", "white", 
"white",    "white", "white", "darkblue", "white", "white", "white", "white", 
"darkgreen", "white"),alpha = 0.50,cex = 1.5, fontfamily = "serif", fontface = "bold",
cat.col = c("darkblue", "darkgreen", "orange", "darkorchid4"), cat.cex = 1.5,
cat.pos = 0, cat.dist = 0.07, cat.fontfamily = "serif", rotation.degree = 270,
margin = 0.2)

VennDiagram - we evaluate the 4-way overlapping on high coverage genes (>5X) between reference bundles.

g1<-c1[c1$Ensembl.Counts>5,1]
g2<-c2[c2$RefSeq.Counts>5,1]
g3<-c3[c3$GencodeComp.Counts>5,1]
g4<-c4[c4$GencodeBasic.Counts>5,1]
venn.plot<-venn.diagram(x = list('Ensembl'=g1,'RefSeq'=g2,'Gencode'=g3,'GencodeBasic'=g4), imagetype='png',filename = "Venn_good_genes.png",
col = "transparent", fill = c("cornflowerblue","green","yellow","darkorchid1"),
label.col = c("orange", "white", "darkorchid4", "white", "white", 
"white",    "white", "white", "darkblue", "white", "white", "white", "white", 
"darkgreen", "white"),alpha = 0.50,cex = 1.5, fontfamily = "serif", fontface = "bold",
cat.col = c("darkblue", "darkgreen", "orange", "darkorchid4"), cat.cex = 1.5,
cat.pos = 0, cat.dist = 0.07, cat.fontfamily = "serif", rotation.degree = 270,
margin = 0.2)
high covered genes

high covered genes

LS0tCnRpdGxlOiAiSW1wYWN0IG9mIHJlZmVyZW5jZSBidW5kbGUgb24gc2NSTkEtU2VxIGdlbmUgcXVhbnRpZmljYXRpb24iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KQWJvdXQgdGhpcyBub3RlYm9vawpUaGlzIG5vdGVib29rIGlzIHBhcnQgb2YgYW5hbHlzaXMgb2YgZXZhbHVhdGUgdGhlIGltcGFjdCBvZiBkaWZmZXJlbnQgcmVmZXJlbmNlIGJ1bmRsZSBvbiBzY1JOQS1TZXEgcXVhbnRpZmljYXRpb24uIAojIEFubm90YXRpb24KRmlyc3Qgd2UgbmVlZCB0byB1c2UgcGFja2FnZSBgcnRyYWNrbGF5ZXJgIHRvIGxvYWQgc2V2ZXJhbCBnZW5lIGFubm90YXRpb24gZ3RmIGZpbGVzLiAKKiBbRW5zZW1ibF0oaHR0cHM6Ly93d3cuZW5zZW1ibC5vcmcvaW5mby9kYXRhL2Z0cC9pbmRleC5odG1sKQoqIFtyZWZzZXFdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcHJvamVjdHMvZ2Vub21lL2d1aWRlL2h1bWFuL2luZGV4LnNodG1sKQoqIFtnZW5jb2RlVjI3XShodHRwczovL3d3dy5nZW5jb2RlZ2VuZXMub3JnL3JlbGVhc2VzL2N1cnJlbnQuaHRtbCkKYGBge3J9CmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCdydHJhY2tsYXllcicpCnN5c3RlbS50aW1lKGd0Zl9nZW5jb2RlX2NvbXAgPC0gcmVhZEdGRigifi9Eb2N1bWVudHMvSENBL3JlZmVyZW5jZS9yZWZzL2dlbmNvZGUudjI3LmNocl9wYXRjaF9oYXBsX3NjYWZmLmFubm90YXRpb24uZ3RmIiwgdmVyc2lvbj0yTCwgdGFncyA9IGMoImdlbmVfbmFtZSIsImdlbmVfaWQiLCAidHJhbnNjcmlwdF9pZCIsImdlbmVfdHlwZSIpKSkKc3lzdGVtLnRpbWUoZ3RmX2dlbmNvZGVfYmFzaWMgPC0gcmVhZEdGRigifi9Eb2N1bWVudHMvSENBL3JlZmVyZW5jZS9yZWZzL2dlbmNvZGUudjI3LmNocl9wYXRjaF9oYXBsX3NjYWZmLmJhc2ljLmFubm90YXRpb24uZ3RmIiwgdmVyc2lvbj0yTCwgdGFncyA9IGMoImdlbmVfbmFtZSIsImdlbmVfaWQiLCAidHJhbnNjcmlwdF9pZCIsImdlbmVfdHlwZSIpKSkKc3lzdGVtLnRpbWUoZ3RmX2Vuc2VtYmwgPC0gcmVhZEdGRigifi9Eb2N1bWVudHMvSENBL3JlZmVyZW5jZS9yZWZzL0hvbW9fc2FwaWVucy5HUkNoMzguOTAuZ3RmIiwgdmVyc2lvbj0yTCx0YWdzID0gYygiZ2VuZV9uYW1lIiwiZ2VuZV9pZCIsICJ0cmFuc2NyaXB0X2lkIiwiZ2VuZV9iaW90eXBlIikpKQpzeXN0ZW0udGltZShndGZfcmVmc2VxIDwtIHJlYWRHRkYoIn4vRG9jdW1lbnRzL0hDQS9yZWZlcmVuY2UvcmVmcy9uY2JpLWdlbm9tZXMtMjAxNy0xMC0wNS9HQ0ZfMDAwMDAxNDA1LjM3X0dSQ2gzOC5wMTFfZ2Vub21pYy5nZmYuZ3oiLCB0YWdzID0gYygiSUQiLCAiTmFtZSIsImdia2V5IiwiZ2VuZSIsImdlbmVfYmlvdHlwZSIsIlBhcmVudCIpKSkKYGBgCgpzb21lIGhlYWRlciBvZiBndGYgZmlsZXMKYGBge3J9CnRhZ3M8LWMoJ0dlbmVJRCcsJ0NocicsJ1N0YXJ0JywnRW5kJywnU3RyYW5kJywnTGVuZ3RoJywnQ291bnRzJykKaGVhZChndGZfZW5zZW1ibCkKYGBgCgojIEltcGFjdCBvbiBnZW5lIHF1YW50aWZpY2F0aW9uIG92ZXJ2aWV3CmEgc2V0IG9mIGdlbmUgcXVhbnRpZmljYXRpb24gcmVzdWx0cyBpcyBzaG93biBhcyBiZWxvdy4gRmlyc3QgdGhlIGdlbmUgY291bnRzIGZyb20gYGVuc2VtYmxgIGJ1bmRsZS4gVGhlIGBTUlIxMjk0OTAwXzI1X0dSQ2gzOF9FbnNlbWJsLmdlbmUudW5xLmNvdW50cy50eHRgIGlzIGdlbmVyYXRlZCBmcm9tIGBmZWF0dXJlQ291bnRzYCB3aXRoIGB1bmlxdWVgIG9wdGlvbgoKIyMgbG9hZCBFbnNlbWJsIGJ1bmRsZSByZXN1bHRzCgpgYGB7cn0KZW5zZW1ibC5jb3VudHM8LXJlYWQuZGVsaW0oJy9Vc2Vycy9qaXNodXh1L0RvY3VtZW50cy9IQ0EvcmVmZXJlbmNlL2NvdW50cy9TUlIxMjk0OTAwXzI1X0dSQ2gzOF9FbnNlbWJsLmdlbmUudW5xLmNvdW50cy50eHQnLHNlcD0nXHQnLGhlYWRlcj1ULHNraXA9MSkKY29sbmFtZXMoZW5zZW1ibC5jb3VudHMpPC10YWdzCm1saXN0PC1tYXRjaChlbnNlbWJsLmNvdW50cyRHZW5lSUQsZ3RmX2Vuc2VtYmwkZ2VuZV9pZCkKZ2VuZU5hbWU8LWd0Zl9lbnNlbWJsJGdlbmVfbmFtZVttbGlzdF0KYnR5cGU8LWd0Zl9lbnNlbWJsJGdlbmVfYmlvdHlwZVttbGlzdF0KYzE8LWRhdGEuZnJhbWUoImdlbmVOYW1lIj1nZW5lTmFtZSwnRW5zZW1ibC5Db3VudHMnPWxvZyhlbnNlbWJsLmNvdW50cyRDb3VudHMrMSxiYXNlPTIpLCdFbnNlbWJsLnR5cGUnPWd0Zl9lbnNlbWJsJGdlbmVfYmlvdHlwZVttbGlzdF0pCmhlYWQoYzEpCmBgYAoKSWYgd2UgZGVmaW5lIGBkZXRlY3RlZCBnZW5lc2AgYXMgZ2VuZXMgd2l0aCBjb3ZlcmFnZSA+MCBhbmQgYGdvb2QgY292ZXJhZ2UgZ2VuZXNgIGFzIGdlbmVzIHdpdGggY292ZXJhZ2UgPjUuIFRoZW4gd2UgY2FuIHBsb3QgdGhlIHRvcCAxMCBiaW90eXBlIGNhdGVnb3JpZXMuIAoKYGBge3J9CmdlbmUuZGV0ZWN0czwtbGlzdCgpCnQxPC1zb3J0KHRhYmxlKGMxW2MxJEVuc2VtYmwuQ291bnRzPjAsM10pLGRlY3JlYXNpbmc9VCkKdDI8LXNvcnQodGFibGUoYzFbYzEkRW5zZW1ibC5Db3VudHM+NSwzXSksZGVjcmVhc2luZz1UKQpnZW5lLmRldGVjdHNbWydlbnNlbWJsJ11dPC1jYmluZCgnZGV0ZWN0ZWQuZ2VuZXMnPXQxWzE6MTBdLCdnb29kLmdlbmVzJz10MlsxOjEwXSkKYGBgCiMjIGxvYWQgYFJlZlNlcWAgYnVuZGxlIHJlc3VsdHMKClRoZW4gdGhlIGdlbmUgY291bnRzIGZyb20gYFJlZlNlcWAuIApgYGB7cn0KcmVmc2VxLmNvdW50czwtcmVhZC5kZWxpbSgnL1VzZXJzL2ppc2h1eHUvRG9jdW1lbnRzL0hDQS9yZWZlcmVuY2UvY291bnRzL1NSUjEyOTQ5MDBfMjVfR1JDaDM4X1JlZlNlcS5nZW5lLnVucS5jb3VudHMudHh0JyxzZXA9J1x0JyxoZWFkZXI9VCxza2lwPTEpCmNvbG5hbWVzKHJlZnNlcS5jb3VudHMpPC10YWdzCm1saXN0PC1tYXRjaChyZWZzZXEuY291bnRzJEdlbmVJRCxndGZfcmVmc2VxJElEKQpnZW5lTmFtZTwtZ3RmX3JlZnNlcSROYW1lW21saXN0XQpjMjwtZGF0YS5mcmFtZSgiZ2VuZU5hbWUiPWdlbmVOYW1lLCJSZWZTZXEuQ291bnRzIj1sb2cocmVmc2VxLmNvdW50cyRDb3VudHMrMSxiYXNlPTIpLCdSZWZTZXEudHlwZSc9Z3RmX3JlZnNlcSRnZW5lX2Jpb3R5cGVbbWxpc3RdKQpoZWFkKGMyKQpgYGAKCnN1bW1hcnkgYGRldGVjdGVkIGdlbmVzYCBhbmQgYGdvb2QgZ2VuZXNgIGJhc2VkIG9uIGJpb3R5cGUuCmBgYHtyfQp0MTwtc29ydCh0YWJsZShjMltjMiRSZWZTZXEuQ291bnRzPjAsM10pLGRlY3JlYXNpbmc9VCkKdDI8LXNvcnQodGFibGUoYzJbYzIkUmVmU2VxLkNvdW50cz41LDNdKSxkZWNyZWFzaW5nPVQpCmdlbmUuZGV0ZWN0c1tbJ3JlZnNlcSddXTwtY2JpbmQoJ2RldGVjdGVkLmdlbmVzJz10MVsxOjEwXSwnZ29vZC5nZW5lcyc9dDJbMToxMF0pCmBgYAojIyBsb2FkIGBnZW5jb2RlYCBidW5kbGUgcmVzdWx0cwoKLSBGb3IgR2VuY29kZVYyNywgdGhlcmUgYXJlIHR3byByZWZlcmVuY2UgYnVuZGxlIGBHZW5jb2RlVjI3IENvbXByZWhhbnNpdmVgIGFuZCBgR2VuY29kZVYyNyBCYXNpY2AuIFJlcGVhdCB0aGUgc2ltaWxhciBhbmFseXNpcy4gCgpgYGB7cn0KZ2VuY29kZS5jb3VudHMuY29tcDwtcmVhZC5kZWxpbSgnL1VzZXJzL2ppc2h1eHUvRG9jdW1lbnRzL0hDQS9yZWZlcmVuY2UvY291bnRzL1NSUjEyOTQ5MDBfMjVfR1JDaDM4X0dlbmNvZGVWMjcuZ2VuZS51bnEuY291bnRzLnR4dCcsc2VwPSdcdCcsaGVhZGVyPVQsc2tpcD0xKQpjb2xuYW1lcyhnZW5jb2RlLmNvdW50cy5jb21wKTwtdGFncwptbGlzdDwtbWF0Y2goZ2VuY29kZS5jb3VudHMuY29tcCRHZW5lSUQsZ3RmX2dlbmNvZGVfY29tcCRnZW5lX2lkKQpnZW5lTmFtZTwtZ3RmX2dlbmNvZGVfY29tcCRnZW5lX25hbWVbbWxpc3RdCmMzPC1kYXRhLmZyYW1lKCJnZW5lTmFtZSI9Z2VuZU5hbWUsIkdlbmNvZGVDb21wLkNvdW50cyI9bG9nKGdlbmNvZGUuY291bnRzLmNvbXAkQ291bnRzKzEsYmFzZT0yKSwnR2VuY29kZS50eXBlJz1ndGZfZ2VuY29kZV9jb21wJGdlbmVfdHlwZVttbGlzdF0pCmRpbShjMykKYGBgCgpgYGB7cn0KZ2VuY29kZS5jb3VudHMuYmFzaWM8LXJlYWQuZGVsaW0oJy9Vc2Vycy9qaXNodXh1L0RvY3VtZW50cy9IQ0EvcmVmZXJlbmNlL2NvdW50cy9TUlIxMjk0OTAwXzI1X0dSQ2gzOF9HZW5jb2RlVjI3X2Jhc2ljLmdlbmUudW5xLmNvdW50cy50eHQnLHNlcD0nXHQnLGhlYWRlcj1ULHNraXA9MSkKY29sbmFtZXMoZ2VuY29kZS5jb3VudHMuYmFzaWMpPC10YWdzCm1saXN0PC1tYXRjaChnZW5jb2RlLmNvdW50cy5iYXNpYyRHZW5lSUQsZ3RmX2dlbmNvZGVfYmFzaWMkZ2VuZV9pZCkKZ2VuZU5hbWU8LWd0Zl9nZW5jb2RlX2Jhc2ljJGdlbmVfbmFtZVttbGlzdF0KYzQ8LWRhdGEuZnJhbWUoImdlbmVOYW1lIj1nZW5lTmFtZSwiR2VuY29kZUJhc2ljLkNvdW50cyI9bG9nKGdlbmNvZGUuY291bnRzLmJhc2ljJENvdW50cysxLGJhc2U9MiksJ0dlbmNvZGUudHlwZSc9Z3RmX2dlbmNvZGVfYmFzaWMkZ2VuZV90eXBlW21saXN0XSkKZGltKGM0KQpgYGAKLSBTdW1tYXJ5IGdlbmUgY292ZXJhZ2UgYmFzZWQgb24gYmlvdHlwZS4gRm9yIGBDb21wcmVoYW5zaXZlIEdlbmNvZGVWMjdgCgpgYGB7cn0KdDE8LXNvcnQodGFibGUoYzNbYzMkR2VuY29kZUNvbXAuQ291bnRzPjAsM10pLGRlY3JlYXNpbmc9VCkKdDI8LXNvcnQodGFibGUoYzNbYzMkR2VuY29kZUNvbXAuQ291bnRzID41LDNdKSxkZWNyZWFzaW5nPVQpCmdlbmUuZGV0ZWN0c1tbJ0dlbmNvZGVDb21wJ11dPC1jYmluZCgnZGV0ZWN0ZWQuZ2VuZXMnPXQxWzE6MTBdLCdnb29kLmdlbmVzJz10MlsxOjEwXSkKYGBgCi0gVGhlbiBgQmFzaWMgR2VuY29kZVYyN2AKCmBgYHtyfQp0MTwtc29ydCh0YWJsZShjNFtjNCRHZW5jb2RlQmFzaWMuQ291bnRzPjAsM10pLGRlY3JlYXNpbmc9VCkKdDI8LXNvcnQodGFibGUoYzRbYzQkR2VuY29kZUJhc2ljLkNvdW50cyA+NSwzXSksZGVjcmVhc2luZz1UKQpnZW5lLmRldGVjdHNbWyJHZW5jb2RlQmFzaWMiXV08LWNiaW5kKCdkZXRlY3RlZC5nZW5lcyc9dDFbMToxMF0sJ2dvb2QuZ2VuZXMnPXQyWzE6MTBdKQpgYGAKIyMgc3VtbWFyeSBhbGwgNCByZWZlcmVuY2UgYnVuZGxlIHRvZ2V0aGVyLiAKCk5vdyB3ZSBjYW4gc3VtbWFyeSBvbiBgZGV0ZWN0ZWQgZ2VuZXNgIGJ5IGBiaW90eXBlYCBjcm9zcyBhbGxsIDQgcmVmZXJlbmNlIGJ1bmRsZS4gCgpgYGB7cn0KIyN0b3AgMyBiaW90eXBlCmxpYnJhcnkocmVzaGFwZTIpCmJpb3M8LWMoJ3Byb3RlaW5fY29kaW5nJywncHJvY2Vzc2VkX3BzZXVkb2dlbmUnLCdwc2V1ZG9nZW5lJywnbGluY1JOQScsJ2xuY1JOQScpCmcuZW5zZW1ibDwtc3Vic2V0KGdlbmUuZGV0ZWN0cyRlbnNlbWJsLHJvd25hbWVzKGdlbmUuZGV0ZWN0cyRlbnNlbWJsKSAlaW4lIGJpb3MpCmcucmVmc2VxPC1zdWJzZXQoZ2VuZS5kZXRlY3RzJHJlZnNlcSxyb3duYW1lcyhnZW5lLmRldGVjdHMkcmVmc2VxKSAlaW4lIGJpb3MpCmcuZ2VuY29kZTwtc3Vic2V0KGdlbmUuZGV0ZWN0cyRHZW5jb2RlQ29tcCxyb3duYW1lcyhnZW5lLmRldGVjdHMkR2VuY29kZUNvbXApICVpbiUgYmlvcykKZy5nZW5jb2RlQmFzaWM8LXN1YnNldChnZW5lLmRldGVjdHMkR2VuY29kZUJhc2ljLHJvd25hbWVzKGdlbmUuZGV0ZWN0cyRHZW5jb2RlQmFzaWMpICVpbiUgYmlvcykKZGV0ZWN0czwtZGF0YS5mcmFtZSgnYmlvdHlwZSc9cmVwKGMoJ3Byb3RlaW5fY29kaW5nJywncHNldWRvZ2VuZScsJ2xuY1JOQScpLDQpLHJiaW5kKGcuZW5zZW1ibCxnLnJlZnNlcSxnLmdlbmNvZGUsZy5nZW5jb2RlQmFzaWMpLCdidW5kbGUnPXJlcChjKCdFbnNlbWJsJywnUmVmU2VxJywnR2VuY29kZScsJ0dlbmNvZGVWMjcnKSxjKDMsMywzLDMpKSkKbS5kZXRlY3RzPC1tZWx0KGRldGVjdHMpCiMjZGV0ZWN0ZWQgPjBYCmdncGxvdChkYXRhPW0uZGV0ZWN0cyxhZXMoeD1iaW90eXBlLHk9dmFsdWUsZmlsbD1idW5kbGUpKStnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIscG9zaXRpb249cG9zaXRpb25fZG9kZ2UoKSkrZmFjZXRfZ3JpZCh2YXJpYWJsZX4uLHNjYWxlcz0iZnJlZV95IikrZ2VvbV90ZXh0KGFlcyhiaW90eXBlLCB2YWx1ZSwgbGFiZWwgPSB2YWx1ZSkscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDEpKSAKCmBgYAoKCiMgRXZhbHVhdGUgYERyb3BvdXRgLCBgb3ZlcmRpc3BlcnNpb25gIGV2ZW50cwoKIyMgIGBFbnNlbWJsYCB2cyBgUmVmU2VxYApXZSBjb21wYXJlIHRoZSBnZW5lIHF1YW50aWZpY2F0aW9uIGZyb20gYEVuc2VtYmxgIGFuZCBgUmVmU2VxYCByZWZlcmVuY2UgYnVuZGxlcy4gV2UgYXJlIGxvb2tpbmcgZm9yIDMgdHlwZSBvZiBldmVudHMsIGBkcm9wLW91dGAgaWYgb25seSBkZXRlY3RhYmxlIGZyb20gb25lIHJlZmVyZW5jZSBidW5kbGUgYnV0IG5vdCBhbm90aGVyLCBgcHJlZGljdGFibGVgIGlmIG9uZSBjYW4gYmUgcHJlZGljdGVkIG9yIGVzdGltYXRlZCBieSBhbm90aGVyIG9yIHRoZSBvYnNlcnZhdGlvbiBpbiBvbmUgcmVzdWx0IGNhbiBmYWxsIGludG8gcHJlZGljdGlvbiBpbnRlcnZhbCBiYXNlZCBvbiByZWdyZXNzaW9uIG1vZGVsLGBvdmVyZGlzcGVyc2lvbmAgaWYgbmVpdGhlciBgZHJvcG91dGAgbm9yIGBwcmVkaWN0YWJsZWAsIGBvdmVyZGlzcGVyc2lvbmAgZ2VuZXJhbGx5IHJlcHJlc2VudCBzb21lIGdlbmVzIGhhdmUgdmVyeSBoaWdoIGV4cHJlc3Npb24gaW4gb25lIHJlc3VsdHMgYnV0IHZlcnkgbG93IGV4cHJlc3Npb24gaW4gYW5vdGhlci4gCi0gIEZpcnN0IG1lcmdlIHR3byBxdWFudGlmaWNhdGlvbiBieSBgZ2VuZU5hbWVgIG9yIGBnZW5lSURgLiAKYGBge3J9CmVuc2VtYmwucmVmc2VxPC1tZXJnZShjMSxjMixieT1jKCdnZW5lTmFtZScpLGFsbD1GLGFsbC54PUYsYWxsLnk9RikKZGltKGVuc2VtYmwucmVmc2VxKQpgYGAKT3ZlcmxhcHBpbmcgZ2VuZXMgSUQgYmV0d2VlbiBFbnNlbWJsIGFuZCBSZWZTZXEgaXMgMzI2NDcuIAotICBMZXQncyBleGFtaW5lIHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIEVuc2VtYmwgYW5kIFJlZlNlcSBxdWFudGlmaWNhdGlvbi4gQmVmb3JlIHRoZSByZWdyZXNzaW9uIGFuYWx5c2lzLCB3ZSBuZWVkIHRvIHJlbW92ZSBgZHJvcC1vdXRgIGdlbmVzIGZyb20gYm90aCBgZW5zZW1ibGAgYW5kIGByZWZzZXFgLiAKYGBge3J9CnN1YmRhdGE8LXN1YnNldChlbnNlbWJsLnJlZnNlcSxFbnNlbWJsLkNvdW50cz4wICYgUmVmU2VxLkNvdW50cyA+MCkKZml0PC1sbShSZWZTZXEuQ291bnRzfkVuc2VtYmwuQ291bnRzLCBkYXRhPXN1YmRhdGEpCnN1bW1hcnkoZml0KQpgYGAKVGhlIGxpbmVhciBtb2RlbCBpcyBwcm9wZXIuIAoKLSBMZXQncyBlc3RpbWF0ZSB0aGUgOTUlIHByZWRpY3Rpb24gaW50ZXJ2YWw6CkZvciBhIGRhdGEgcG9pbnQKYGBge3J9CnByZWQ8LXByZWRpY3QoZml0LGRhdGE9ZW5zZW1ibC5yZWZzZXEsbGV2ZWw9MCkKY29uZl9pbnRlcnZhbF8zIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhPWRhdGEuZnJhbWUoRW5zZW1ibC5Db3VudHM9MTAwKSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLGxldmVsID0gMC45NSkKY29uZl9pbnRlcnZhbF8zCmBgYAoKVGhlbiBmb3IgcmVncmVzc2lvbiBsaW5lCgpgYGB7cn0KY29uZl9pbnRlcnZhbCA8LSBwcmVkaWN0KGZpdCwgbmV3ZGF0YT1lbnNlbWJsLnJlZnNlcSwgaW50ZXJ2YWw9InByZWRpY3Rpb24iLGxldmVsID0gMC45NSkKYGBgCklmIGRhdGEgcG9pbnQgY2FuIG5vdCBmYWxsIGludG8gdGhpcyA5NSUgcHJlZGljdGlvbiBpbnRlcnZhbHMsIGl0IHNob3VsZCBiZSBjb3VudGVkIGFzIGBvdmVyZGlzcGVyc2lvbmAKLSBsZXQncyBwbG90IHJlc3VsdHMgd2l0aCBzY2F0dGVycGxvdApgYGB7cn0KIyN0aGlzIGZ1bmN0aW9uIGp1c3QgbGFiZWwgbW9kZWwgaW4gcGxvdAplcXVhdGlvbiA9IGZ1bmN0aW9uKHgpIHsKICBsbV9jb2VmIDwtIGxpc3QoYSA9IHJvdW5kKGNvZWYoeClbMV0sIGRpZ2l0cyA9IDIpLAogICAgICAgICAgICAgICAgICBiID0gcm91bmQoY29lZih4KVsyXSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgICAgICAgIHIyID0gcm91bmQoc3VtbWFyeSh4KSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDIpKTsKICBsbV9lcSA8LSBzdWJzdGl0dXRlKGl0YWxpYyh5KSA9PSBhICsgYiAlLiUgaXRhbGljKHgpKiIsIn5+aXRhbGljKFIpXjJ+Ij0ifnIyLGxtX2NvZWYpCiAgYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24obG1fZXEpKTsgICAgICAgICAgICAgICAgIAp9CiMjbGFiZWwgd2l0aCBjb2xvcgp0eXBlPC1yZXAoJ25vcm1hbCcsbnJvdyhlbnNlbWJsLnJlZnNlcSkpCnR5cGVbZW5zZW1ibC5yZWZzZXEkRW5zZW1ibC5Db3VudHM9PTAgJiBlbnNlbWJsLnJlZnNlcSRSZWZTZXEuQ291bnRzPjBdPC0nZHJvcG91dC5lbnNlbWJsJwp0eXBlW2Vuc2VtYmwucmVmc2VxJEVuc2VtYmwuQ291bnRzPjAgJiBlbnNlbWJsLnJlZnNlcSRSZWZTZXEuQ291bnRzPT0wXTwtJ2Ryb3BvdXQucmVmc2VxJwp0eXBlWyhlbnNlbWJsLnJlZnNlcSRSZWZTZXEuQ291bnRzID4gY29uZl9pbnRlcnZhbFssM118ZW5zZW1ibC5yZWZzZXEkUmVmU2VxLkNvdW50czxjb25mX2ludGVydmFsWywyXSkmKGVuc2VtYmwucmVmc2VxJEVuc2VtYmwuQ291bnRzPjAgJiBlbnNlbWJsLnJlZnNlcSRSZWZTZXEuQ291bnRzPjApXTwtJ292ZXJkaXNwZXJzaW9uJyAKZW5zZW1ibC5yZWZzZXEkdHlwZTwtdHlwZQojIyBsYWJlbCBwcmVmaWN0aW9uIG1vZGVsIGluIHBsb3QKZXEgPC0gc3Vic3RpdHV0ZShpdGFsaWMoeSkgPT0gYSArIGIgJS4lIGl0YWxpYyh4KSoiLCJ+fml0YWxpYyhyKV4yfiI9In5yMiwgbGlzdChhID0gZm9ybWF0KGNvZWYoZml0KVsxXSwgZGlnaXRzID0gMiksIGIgPSBmb3JtYXQoY29lZihmaXQpWzJdLCBkaWdpdHMgPSAyKSwgcjIgPSBmb3JtYXQoc3VtbWFyeShmaXQpJHIuc3F1YXJlZCwgZGlnaXRzID0gMykpKQojIyBzY2F0dGVyIHBsb3QKcDEgPC0gZ2dwbG90KGVuc2VtYmwucmVmc2VxLGFlcyh4PUVuc2VtYmwuQ291bnRzLHk9UmVmU2VxLkNvdW50cyksY29sb3I9ImdyZXkiKQpwMTwtcDErZ2VvbV9yaWJib24oYWVzKHltaW49Y29uZl9pbnRlcnZhbFssMl0seW1heD1jb25mX2ludGVydmFsWywzXSksYWxwaGE9MC4yLGZpbGw9InJlZCIpCnAxPC1wMStnZW9tX3BvaW50KGRhdGE9ZW5zZW1ibC5yZWZzZXEsYWVzKHg9RW5zZW1ibC5Db3VudHMseT1SZWZTZXEuQ291bnRzLGNvbG9yPXR5cGUpKQpwMTwtcDErYW5ub3RhdGUoInRleHQiLCB4ID0gNC4xLCB5ID0gMTIsIGxhYmVsID0gZXF1YXRpb24oZml0KSwgcGFyc2UgPSBUUlVFKQpwMTwtcDEreGxhYigibG9nMihFbnNlbWJsLkNvdW50cysxKSIpK3lsYWIoImxvZzIoUmVmU2VxKzEpIikKcDEKYGBgCgotIENvdW50IGRyb3Atb3V0IGdlbmVzIGluIGBFbnNlbWJsYCBjb21wYXJlZCB0byBgUmVmU2VxYAoKYGBge3J9CmRyb3BvdXQuZW5zZW1ibDwtc3Vic2V0KGVuc2VtYmwucmVmc2VxLEVuc2VtYmwuQ291bnRzPT0wICYgUmVmU2VxLkNvdW50cz4wKQpkaW0oZHJvcG91dC5lbnNlbWJsKQojIyBiYXJwbG90Cng8LWRhdGEuZnJhbWUoc29ydCh0YWJsZShkcm9wb3V0LmVuc2VtYmwkRW5zZW1ibC50eXBlKSxkZWNyZWFzaW5nPVQpWzE6MTBdKQpjb2xuYW1lcyh4KTwtYygnQmlvdHlwZScsJ0NvdW50cycpCmdncGxvdChkYXRhLmZyYW1lKHgpLGFlcyh4PUJpb3R5cGUseT1Db3VudHMpKStnZW9tX2JhcihhZXMoZmlsbCA9IEJpb3R5cGUpLCBwb3NpdGlvbiA9ICJkb2RnZSIsIHN0YXQ9ImlkZW50aXR5IikreGxhYignQmlvdHlwZScpK3lsYWIoIiMgb2YgZHJvcG91dCBnZW5lcyIpK2dlb21fdGV4dChhZXMobGFiZWw9Q291bnRzKSwgdmp1c3Q9MS42LCBjb2xvcj0iYmxhY2siLCBzaXplPTMuNSkgK3RoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkrZ2d0aXRsZSgnZHJvcG91dCBnZW5lcyBpbiBFbnNlbWJsJykKYGBgCi0gV2Ugd2FudCB0byBrbm93IHdoZXRoZXIgdGhlIGBkcm9wb3V0YCBnZW5lcyBpbiBFbnNlbWJsIGhhdmUgYW55IGJpb2xvZ2ljYWwgZnVuY3Rpb25hbCBtZWFuaW5nLiBXZSBvdmVybGFwIGBkcm9wb3V0YCBnZW5lcyB3aXRoIE1TaWdEQiBgQzJfY3VyYXRlZGAgZ2VuZXNldCwgYEMyX2N1cmF0ZWRgIGdlbmVzZXQgY29udGFpbiBtb3N0IGBLRUdHYCBwYXRod2F5LiB0d28gd2F5IHRvIGNhbGN1bGF0ZSB0aGUgZ2VuZXNldCBvdmVybGFwcGluZywgRmlzaGVyLWV4YWN0IHRlc3QgYW5kIGh5cGVyLWdlb21ldHJ5IHRlc3QuIFdlIHVzZSBib3RoIHRlc3RzLiAKCmBgYHtyfQpsaWJyYXJ5KEdlbmVPdmVybGFwKQpsaWJyYXJ5KE1TaWdEQikKIyMgY3JlYXRlIGZ1bmN0aW9uIHRvIGRvIG92ZXJsYXBwaW5nIHRlc3QKZ3NPdmVybGFwPC1mdW5jdGlvbihnc19jdXJlYXRlZCxncyl7CiAgIyNtc2lnZGJfYzI8LU1TaWdEQltbIkMyX0NVUkFURUQiXV0KICBwdmFsczwtYygpCiAgZm9yKGkgaW4gbmFtZXMoZ3NfY3VyYXRlZCkpewogICAgZ28ub2JqIDwtIG5ld0dlbmVPdmVybGFwKGdzX2N1cmF0ZWRbW2ldXSwgZ3MsIGdlbm9tZS5zaXplPTYwMDAwKQogICAgIyNmaXNoZXIgZXhhY3QKICAgIGdvLm9iaiA8LSB0ZXN0R2VuZU92ZXJsYXAoZ28ub2JqKQogICAgb3ZlcmxhcDwtbGVuZ3RoKGdldEludGVyc2VjdGlvbihnby5vYmopKQogICAgUG9wU2l6ZTwtNjAwMDAKICAgIGYucHZhbDwtZ2V0UHZhbChnby5vYmopCiAgICBsaXN0MTwtbGVuZ3RoKGdzKQogICAgbGlzdDI8LWxlbmd0aChnc19jdXJhdGVkW1tpXV0pCiAgICAjI2h5cGVyR1QKICAgIGlmKG92ZXJsYXA+MSl7CiAgICAgaC5wdmFsPC1waHlwZXIob3ZlcmxhcC0xLGxpc3QxLFBvcFNpemUtbGlzdDEsbGlzdDIsbG93ZXIudGFpbCA9IEZBTFNFLCBsb2cucCA9IEZBTFNFKQogIH1lbHNlewogICAgaC5wdmFsPC0xCiAgICB9CiAgICBwdmFsczwtcmJpbmQocHZhbHMsYyhpLG92ZXJsYXAsbGlzdDEsbGlzdDIsZi5wdmFsLGgucHZhbCkpCiAgfQogIGNvbG5hbWVzKHB2YWxzKTwtYygnZ3NOYW1lJywnb3ZlcmxhcCcsJ2Ryb3BvdXQuZ3MubGVuZ3RoJywnbXNpZ2RiLmdzLmxlbmd0aCcsJ2Zpc2hlcicsJ2h5cGVyJykKICBwdmFsczwtZGF0YS5mcmFtZShwdmFscykKICBwdmFscyRmaXNoZXIuYWRqPC1wLmFkanVzdChwdmFscyRmaXNoZXIpCiAgcHZhbHMkaHlwZXIuYWRqPC1wLmFkanVzdChwdmFscyRoeXBlcikKICByZXR1cm4ocHZhbHMpCn0KCiMjIGRvIHRlc3QgOiBlbnNlbWJsCmdzX2N1cmF0ZWQ8LU1TaWdEQltbIkMyX0NVUkFURUQiXV0KZ3M8LWRyb3BvdXQuZW5zZW1ibCRnZW5lTmFtZQpyZXM8LWdzT3ZlcmxhcChnc19jdXJhdGVkLGdzKQpoZWFkKHJlcykKc3VtKHJlcyRmaXNoZXIuYWRqPDAuMDUpCnN1bShyZXMkaHlwZXIuYWRqPDAuMDUpCiMjIGh5cGhlckd0ZXN0CmBgYApTbyB0aGVyZSBpcyBubyBzaWduaWZpY2FudCBlbnJpY2htZW50IHdpdGggYW55IGdlbmVzZXRzIGluIEMyLgpBbmQgcmVwZWF0IHRoaXMgYW5hbHlzaXMgd2l0aCB0aGUgYGRyb3BvdXRgIGdlbmVzIGluIFJlZlNlcQpgYGB7cn0KZHJvcG91dC5yZWZzZXE8LXN1YnNldChlbnNlbWJsLnJlZnNlcSxFbnNlbWJsLkNvdW50cz4wICYgUmVmU2VxLkNvdW50cz09MCkKZGltKGRyb3BvdXQucmVmc2VxKQojI2JhcnBsb3Qoc29ydCh0YWJsZShkcm9wb3V0LnJlZnNlcSRSZWZTZXEudHlwZSksZGVjcmVhc2luZz1UKVsxOjEwXSxtYWluPSJEcm9wb3V0IGdlbmVzOiBSZWZTZXEiKQp4PC1kYXRhLmZyYW1lKHNvcnQodGFibGUoZHJvcG91dC5yZWZzZXEkUmVmU2VxLnR5cGUpLGRlY3JlYXNpbmc9VClbMToxMF0pCmNvbG5hbWVzKHgpPC1jKCdCaW90eXBlJywnQ291bnRzJykKZ2dwbG90KGRhdGEuZnJhbWUoeCksYWVzKHg9QmlvdHlwZSx5PUNvdW50cykpK2dlb21fYmFyKGFlcyhmaWxsID0gQmlvdHlwZSksIHBvc2l0aW9uID0gImRvZGdlIiwgc3RhdD0iaWRlbnRpdHkiKSt4bGFiKCdCaW90eXBlJykreWxhYigiIyBvZiBkcm9wb3V0IGdlbmVzIikrZ2VvbV90ZXh0KGFlcyhsYWJlbD1Db3VudHMpLCB2anVzdD0xLjYsIGNvbG9yPSJibGFjayIsIHNpemU9My41KSArdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpKStnZ3RpdGxlKCdkcm9wb3V0IGdlbmVzIGluIFJlZlNlcScpCmBgYApSZWZTZXEncyBgZHJvcG91dGAgZ2VuZXMgaGF2ZSBhbnkgZnVuY3Rpb25hbCBtZWFuaW5nPyAKYGBge3J9CmdzPC1kcm9wb3V0LnJlZnNlcSRnZW5lTmFtZQpyZXM8LWdzT3ZlcmxhcChnc19jdXJhdGVkLGdzKQpoZWFkKHJlcykKc3VtKHJlcyRmaXNoZXIuYWRqPDAuMDUpCnN1bShyZXMkaHlwZXIuYWRqPDAuMDUpCmBgYAoKIyMgYEdlbmNvZGVWMjdCYXNpY2AgdnMgYHJlZlNlcWAKLSAgV2UgcmVwZWF0IHRoZSBhbmFseXNpcyB0byBjb21wYXJlIGBHZW5jb2RlVjI3QmFzaWNgIHdpdGggYFJlZlNlcWAuIApgYGB7cn0KZ2VuY29kZS5yZWZzZXE8LW1lcmdlKGMyLGM0LGJ5PWMoJ2dlbmVOYW1lJyksYWxsPUYsIGFsbC54PUYsYWxsLnk9RikKZGltKGdlbmNvZGUucmVmc2VxKQpgYGAKLSAgUmVncmVzc2lvbiBtb2RlbCAKYGBge3J9CnN1YmRhdGE8LXN1YnNldChnZW5jb2RlLnJlZnNlcSxHZW5jb2RlQmFzaWMuQ291bnRzPjAgJiBSZWZTZXEuQ291bnRzID4wKQpmaXQ8LWxtKFJlZlNlcS5Db3VudHN+R2VuY29kZUJhc2ljLkNvdW50cywgZGF0YT1zdWJkYXRhKQpzdW1tYXJ5KGZpdCkKYGBgCgotIGVzdGltYXRlIDk1JSBwcmVkaWN0aW9uIGludGVydmFsLiBGb3IgYSBkYXRhIHBvaW50CmBgYHtyfQpwcmVkPC1wcmVkaWN0KGZpdCxkYXRhPWdlbmNvZGUucmVmc2VxKQpjb25mX2ludGVydmFsXzMgPC0gcHJlZGljdChmaXQsIG5ld2RhdGE9ZGF0YS5mcmFtZShHZW5jb2RlQmFzaWMuQ291bnRzPTEwMCksIGludGVydmFsPSJwcmVkaWN0aW9uIixsZXZlbCA9IDAuOTUpCmNvbmZfaW50ZXJ2YWxfMwpgYGAKCjk1JSBwcmVkaWN0aW9uIGludGVydmFscyBmb3IgcmVncmVzc2lvbiBsaW5lCmBgYHtyfQpjb25mX2ludGVydmFsIDwtIHByZWRpY3QoZml0LCBuZXdkYXRhPWdlbmNvZGUucmVmc2VxLCBpbnRlcnZhbD0icHJlZGljdGlvbiIsbGV2ZWwgPSAwLjk1KQpgYGAKLSAgdmlzdWFsaXplIHJlc3VsdHMgaW4gc2NhdHRlcnBsb3QKYGBge3J9CiMjbGFiZWwgd2l0aCBjb2xvcgp0eXBlPC1yZXAoJ25vcm1hbCcsbnJvdyhnZW5jb2RlLnJlZnNlcSkpCnR5cGVbZ2VuY29kZS5yZWZzZXEkR2VuY29kZUJhc2ljLkNvdW50cz09MCAmIGdlbmNvZGUucmVmc2VxJFJlZlNlcS5Db3VudHM+MF08LSdkcm9wb3V0LmdlbmNvZGUnCnR5cGVbZ2VuY29kZS5yZWZzZXEkR2VuY29kZUJhc2ljLkNvdW50cz4wICYgZ2VuY29kZS5yZWZzZXEkUmVmU2VxLkNvdW50cz09MF08LSdkcm9wb3V0LnJlZnNlcScKdHlwZVsoZ2VuY29kZS5yZWZzZXEkUmVmU2VxLkNvdW50cyA+IGNvbmZfaW50ZXJ2YWxbLDNdIHwgZ2VuY29kZS5yZWZzZXEkUmVmU2VxLkNvdW50czxjb25mX2ludGVydmFsWywyXSkmKGdlbmNvZGUucmVmc2VxJEdlbmNvZGVCYXNpYy5Db3VudHM+MCAmIGdlbmNvZGUucmVmc2VxJFJlZlNlcS5Db3VudHM+MCldPC0nb3ZlcmRpc3BlcnNpb24nCmdlbmNvZGUucmVmc2VxJHR5cGU8LXR5cGUKdGFibGUoZ2VuY29kZS5yZWZzZXEkdHlwZSkKIyMgbGFiZWwgcHJlZmljdGlvbiBtb2RlbCBpbiBwbG90CmVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsIGxpc3QoYSA9IGZvcm1hdChjb2VmKGZpdClbMV0sIGRpZ2l0cyA9IDIpLCBiID0gZm9ybWF0KGNvZWYoZml0KVsyXSwgZGlnaXRzID0gMiksIHIyID0gZm9ybWF0KHN1bW1hcnkoZml0KSRyLnNxdWFyZWQsIGRpZ2l0cyA9IDMpKSkKIyMgc2NhdHRlciBwbG90CnAxIDwtIGdncGxvdChnZW5jb2RlLnJlZnNlcSxhZXMoeD1HZW5jb2RlQmFzaWMuQ291bnRzLHk9UmVmU2VxLkNvdW50cyksY29sb3I9ImdyZXkiKQpwMTwtcDErZ2VvbV9yaWJib24oYWVzKHltaW49Y29uZl9pbnRlcnZhbFssMl0seW1heD1jb25mX2ludGVydmFsWywzXSksYWxwaGE9MC4yLGZpbGw9InJlZCIpCnAxPC1wMStnZW9tX3BvaW50KGRhdGE9Z2VuY29kZS5yZWZzZXEsYWVzKHg9R2VuY29kZUJhc2ljLkNvdW50cyx5PVJlZlNlcS5Db3VudHMsY29sb3I9dHlwZSkpCnAxPC1wMSthbm5vdGF0ZSgidGV4dCIsIHggPSA0LjEsIHkgPSAxMiwgbGFiZWwgPSBlcXVhdGlvbihmaXQpLCBwYXJzZSA9IFRSVUUpCnAxPC1wMSt4bGFiKCJsb2cyKEdlbmNvZGVCYXNpYy5Db3VudHMrMSkiKSt5bGFiKCJsb2cyKFJlZlNlcS5Db3VudHMrMSkiKQpwMQpgYGAKCi0gIENvdW50IGRyb3Atb3V0IGdlbmVzIGluIGBHZW5jb2RlVjI3IGJhc2ljYCBjb21wYXJlZCB0byBgUmVmU2VxYAoKYGBge3J9CmRyb3BvdXQuZ2VuY29kZTwtc3Vic2V0KGdlbmNvZGUucmVmc2VxLEdlbmNvZGVCYXNpYy5Db3VudHM9PTAgJiBSZWZTZXEuQ291bnRzPjApCmRpbShkcm9wb3V0LmdlbmNvZGUpCiNiYXJwbG90KHNvcnQodGFibGUoZHJvcG91dC5nZW5jb2RlJEdlbmNvZGUudHlwZSksZGVjcmVhc2luZz1UKVsxOjEwXSxtYWluPSJEcm9wb3V0IGdlbmVzOiBHZW5jb2RlQmFzaWMiKQp4PC1kYXRhLmZyYW1lKHNvcnQodGFibGUoZHJvcG91dC5nZW5jb2RlJEdlbmNvZGUudHlwZSksZGVjcmVhc2luZz1UKVsxOjEwXSkKY29sbmFtZXMoeCk8LWMoJ0Jpb3R5cGUnLCdDb3VudHMnKQpnZ3Bsb3QoZGF0YS5mcmFtZSh4KSxhZXMoeD1CaW90eXBlLHk9Q291bnRzKSkrZ2VvbV9iYXIoYWVzKGZpbGwgPSBCaW90eXBlKSwgcG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpK3hsYWIoJ0Jpb3R5cGUnKSt5bGFiKCIjIG9mIGRyb3BvdXQgZ2VuZXMiKStnZW9tX3RleHQoYWVzKGxhYmVsPUNvdW50cyksIHZqdXN0PTEuNiwgY29sb3I9ImJsYWNrIiwgc2l6ZT0zLjUpICt0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpK2dndGl0bGUoJ2Ryb3BvdXQgZ2VuZSBpbiBHZW5jb2RlJykKYGBgCndoZXRoZXIgYGRyb3BvdXRgIGdlbmVzIGhhdmUgYW55IGZ1bmN0aW9uYWwgbWVhbmluZz8KYGBge3J9CmdzPC1kcm9wb3V0LmdlbmNvZGUkZ2VuZU5hbWUKZ3NfY3VyYXRlZDwtTVNpZ0RCW1siQzJfQ1VSQVRFRCJdXQpyZXM8LWdzT3ZlcmxhcChnc19jdXJhdGVkLGdzKQpoZWFkKHJlcykKc3VtKHJlcyRmaXNoZXIuYWRqPDAuMDUpCnN1bShyZXMkaHlwZXIuYWRqPDAuMDUpCmBgYAoKVGhlbiB0aGUgZHJvcC1vdXQgZ2VuZXMgaW4gYFJlZlNlcWAgY29tcGFyZWQgdG8gYEdlbmNvZGVWMjcgQmFzaWNgCgpgYGB7cn0KZHJvcG91dC5yZWZzZXE8LXN1YnNldChnZW5jb2RlLnJlZnNlcSxHZW5jb2RlQmFzaWMuQ291bnRzPjAgJiBSZWZTZXEuQ291bnRzPT0wKQpkaW0oZHJvcG91dC5yZWZzZXEpCiMjYmFycGxvdChzb3J0KHRhYmxlKGRyb3BvdXQucmVmc2VxJFJlZlNlcS50eXBlKSxkZWNyZWFzaW5nPVQpWzE6MTBdLG1haW49IkRyb3BvdXQgZ2VuZXM6IFJlZlNlcSIpCng8LWRhdGEuZnJhbWUoc29ydCh0YWJsZShkcm9wb3V0LnJlZnNlcSRSZWZTZXEudHlwZSksZGVjcmVhc2luZz1UKVsxOjEwXSkKY29sbmFtZXMoeCk8LWMoJ0Jpb3R5cGUnLCdDb3VudHMnKQpnZ3Bsb3QoZGF0YS5mcmFtZSh4KSxhZXMoeD1CaW90eXBlLHk9Q291bnRzKSkrZ2VvbV9iYXIoYWVzKGZpbGwgPSBCaW90eXBlKSwgcG9zaXRpb24gPSAiZG9kZ2UiLCBzdGF0PSJpZGVudGl0eSIpK3hsYWIoJ0Jpb3R5cGUnKSt5bGFiKCIjIG9mIGRyb3BvdXQgZ2VuZXMiKStnZW9tX3RleHQoYWVzKGxhYmVsPUNvdW50cyksIHZqdXN0PTEuNiwgY29sb3I9ImJsYWNrIiwgc2l6ZT0zLjUpICt0aGVtZShheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpK2dndGl0bGUoJ2Ryb3BvdXQgZ2VuZXMgaW4gcmVmc2VxJykKYGBgCldoZXRoZXIgYGRyb3BvdXRgIGdlbmVzIGhhdmUgZnVuY3Rpb25hbCBtZWFuaW5nPwpgYGB7cn0KZ3M8LWRyb3BvdXQucmVmc2VxJGdlbmVOYW1lCmdzX2N1cmF0ZWQ8LU1TaWdEQltbIkMyX0NVUkFURUQiXV0KcmVzPC1nc092ZXJsYXAoZ3NfY3VyYXRlZCxncykKaGVhZChyZXMpCnN1bShyZXMkZmlzaGVyLmFkajwwLjA1KQpzdW0ocmVzJGh5cGVyLmFkajwwLjA1KQpgYGAKCiMgNC13YXlzIG92ZXJsYXBwaW5nIAoKLSBOZXh0LCB3ZSBldmFsdWF0ZSB0aGUgNC13YXkgb3ZlcmxhcHBpbmcgb24gYGRlY3RlY3RlZCBnZW5lc2AgYmV0d2VlbiByZWZlcmVuY2UgYnVuZGxlcy4KCmBgYHtyfQpsaWJyYXJ5KCdWZW5uRGlhZ3JhbScpCmcxPC1jMVtjMSRFbnNlbWJsLkNvdW50cz4wLDFdCmcyPC1jMltjMiRSZWZTZXEuQ291bnRzPjAsMV0KZzM8LWMzW2MzJEdlbmNvZGVDb21wLkNvdW50cz4wLDFdCmc0PC1jNFtjNCRHZW5jb2RlQmFzaWMuQ291bnRzPjAsMV0KdmVubi5wbG90PC12ZW5uLmRpYWdyYW0oeCA9IGxpc3QoJ0Vuc2VtYmwnPWcxLCdSZWZTZXEnPWcyLCdHZW5jb2RlJz1nMywnR2VuY29kZUJhc2ljJz1nNCksIGltYWdldHlwZT0ncG5nJyxmaWxlbmFtZSA9ICJWZW5uLnBuZyIsCmNvbCA9ICJ0cmFuc3BhcmVudCIsIGZpbGwgPSBjKCJjb3JuZmxvd2VyYmx1ZSIsImdyZWVuIiwieWVsbG93IiwiZGFya29yY2hpZDEiKSwKbGFiZWwuY29sID0gYygib3JhbmdlIiwgIndoaXRlIiwgImRhcmtvcmNoaWQ0IiwgIndoaXRlIiwgIndoaXRlIiwgCiJ3aGl0ZSIsICAgICJ3aGl0ZSIsICJ3aGl0ZSIsICJkYXJrYmx1ZSIsICJ3aGl0ZSIsICJ3aGl0ZSIsICJ3aGl0ZSIsICJ3aGl0ZSIsIAoiZGFya2dyZWVuIiwgIndoaXRlIiksYWxwaGEgPSAwLjUwLGNleCA9IDEuNSwgZm9udGZhbWlseSA9ICJzZXJpZiIsIGZvbnRmYWNlID0gImJvbGQiLApjYXQuY29sID0gYygiZGFya2JsdWUiLCAiZGFya2dyZWVuIiwgIm9yYW5nZSIsICJkYXJrb3JjaGlkNCIpLCBjYXQuY2V4ID0gMS41LApjYXQucG9zID0gMCwgY2F0LmRpc3QgPSAwLjA3LCBjYXQuZm9udGZhbWlseSA9ICJzZXJpZiIsIHJvdGF0aW9uLmRlZ3JlZSA9IDI3MCwKbWFyZ2luID0gMC4yKQoKYGBgCiFbVmVubkRpYWdyYW1dKFZlbm4ucG5nKQotICB3ZSBldmFsdWF0ZSB0aGUgNC13YXkgb3ZlcmxhcHBpbmcgb24gYGhpZ2ggY292ZXJhZ2UgZ2VuZXMgKD41WClgIGJldHdlZW4gcmVmZXJlbmNlIGJ1bmRsZXMuCgpgYGB7cn0KZzE8LWMxW2MxJEVuc2VtYmwuQ291bnRzPjUsMV0KZzI8LWMyW2MyJFJlZlNlcS5Db3VudHM+NSwxXQpnMzwtYzNbYzMkR2VuY29kZUNvbXAuQ291bnRzPjUsMV0KZzQ8LWM0W2M0JEdlbmNvZGVCYXNpYy5Db3VudHM+NSwxXQp2ZW5uLnBsb3Q8LXZlbm4uZGlhZ3JhbSh4ID0gbGlzdCgnRW5zZW1ibCc9ZzEsJ1JlZlNlcSc9ZzIsJ0dlbmNvZGUnPWczLCdHZW5jb2RlQmFzaWMnPWc0KSwgaW1hZ2V0eXBlPSdwbmcnLGZpbGVuYW1lID0gIlZlbm5fZ29vZF9nZW5lcy5wbmciLApjb2wgPSAidHJhbnNwYXJlbnQiLCBmaWxsID0gYygiY29ybmZsb3dlcmJsdWUiLCJncmVlbiIsInllbGxvdyIsImRhcmtvcmNoaWQxIiksCmxhYmVsLmNvbCA9IGMoIm9yYW5nZSIsICJ3aGl0ZSIsICJkYXJrb3JjaGlkNCIsICJ3aGl0ZSIsICJ3aGl0ZSIsIAoid2hpdGUiLCAgICAid2hpdGUiLCAid2hpdGUiLCAiZGFya2JsdWUiLCAid2hpdGUiLCAid2hpdGUiLCAid2hpdGUiLCAid2hpdGUiLCAKImRhcmtncmVlbiIsICJ3aGl0ZSIpLGFscGhhID0gMC41MCxjZXggPSAxLjUsIGZvbnRmYW1pbHkgPSAic2VyaWYiLCBmb250ZmFjZSA9ICJib2xkIiwKY2F0LmNvbCA9IGMoImRhcmtibHVlIiwgImRhcmtncmVlbiIsICJvcmFuZ2UiLCAiZGFya29yY2hpZDQiKSwgY2F0LmNleCA9IDEuNSwKY2F0LnBvcyA9IDAsIGNhdC5kaXN0ID0gMC4wNywgY2F0LmZvbnRmYW1pbHkgPSAic2VyaWYiLCByb3RhdGlvbi5kZWdyZWUgPSAyNzAsCm1hcmdpbiA9IDAuMikKYGBgCgohW2hpZ2ggY292ZXJlZCBnZW5lc10oVmVubl9nb29kX2dlbmVzLnBuZykK